 /*******************************************************************************
  * Copyright (c) 2003, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.osgi.framework.internal.defaultadaptor;

 import java.io.File ;
 import java.io.IOException ;
 import java.security.AccessController ;
 import java.security.PrivilegedActionException ;
 import java.security.PrivilegedExceptionAction ;
 import java.util.ArrayList ;
 import org.eclipse.osgi.framework.adaptor.BundleData;
 import org.eclipse.osgi.framework.adaptor.core.*;
 import org.eclipse.osgi.framework.debug.Debug;
 import org.eclipse.osgi.framework.log.FrameworkLog;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkEvent;

 /**
  * The DefaultAdaptor for the Framework. This adaptor uses
  * root bundle store directory on the local filesystem to
  * store bundle files and bundle data.
  * <p>
  * Each bundle installed in the Framework will have a unique
  * directory using the bundle ID as the name. Each bundles
  * unique directory has a generational directory a data directory
  * and a metadata file.
  * <p>
  * The generational directory is used to store the different
  * versions of the bundle that have been installed. Each time
  * the bundle is updated a new generational directory will be
  * created.
  * <p>
  * The data directory is used to create data file objects requested
  * by the bundle. This directory will not change when updating
  * a bundle
  * <p>
  * The metadata file contains persistent data about the bundle
  * (e.g. startlevel, persistent start state, etc)
  */
 public class DefaultAdaptor extends AbstractFrameworkAdaptor {
     public static final String METADATA_ADAPTOR_NEXTID = "METADATA_ADAPTOR_NEXTID"; //$NON-NLS-1$
 public static final String METADATA_ADAPTOR_IBSL = "METADATA_ADAPTOR_IBSL"; //$NON-NLS-1$

     public static final String METADATA_BUNDLE_GEN = "METADATA_BUNDLE_GEN"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_LOC = "METADATA_BUNDLE_LOC"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_REF = "METADATA_BUNDLE_REF"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_NAME = "METADATA_BUNDLE_NAME"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_NCP = "METADATA_BUNDLE_NCP"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_ABSL = "METADATA_BUNDLE_ABSL"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_STATUS = "METADATA_BUNDLE_STATUS"; //$NON-NLS-1$
 public static final String METADATA_BUNDLE_METADATA = "METADATA_BUNDLE_METADATA"; //$NON-NLS-1$
 public static final String METADATA_LAST_MODIFIED = "METADATA_LAST_MODIFIED"; //$NON-NLS-1$

     /**
      * The MetaData for the default adaptor
      */
     protected MetaData fwMetadata;

     public DefaultAdaptor(String [] args) {
         super(args);
     }

     /**
      * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getInstalledBundles()
      */
     public BundleData[] getInstalledBundles() {
         String list[] = getBundleStoreRootDir().list();

         if (list == null) {
             return null;
         }
         ArrayList bundleDatas = new ArrayList (list.length);

         /* create bundle objects for all installed bundles. */
         for (int i = 0; i < list.length; i++) {
             try {
                 DefaultBundleData data;

                 long id = -1;
                 try {
                     id = Long.parseLong(list[i]);
                 } catch (NumberFormatException nfe) {
                     continue;
                 }
                 data = (DefaultBundleData) getElementFactory().createBundleData(this, id);
                 loadMetaDataFor(data);
                 data.initializeExistingBundle();

                 if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                     Debug.println("BundleData created: " + data); //$NON-NLS-1$
 }
                 processExtension(data, EXTENSION_INITIALIZE);
                 bundleDatas.add(data);
             } catch (BundleException e) {
                 if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                     Debug.println("Unable to open Bundle[" + list[i] + "]: " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
 Debug.printStackTrace(e);
                 }
             } catch (IOException e) {
                 if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                     Debug.println("Unable to open Bundle[" + list[i] + "]: " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
 Debug.printStackTrace(e);
                 }
             }
         }

         return (BundleData[]) bundleDatas.toArray(new BundleData[bundleDatas.size()]);
     }

     public void setInitialBundleStartLevel(int value) {
         super.setInitialBundleStartLevel(value);
         try {
             persistInitialBundleStartLevel(value);
         } catch (IOException e) {
             eventPublisher.publishFrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e);
         }
     }

     protected void persistInitialBundleStartLevel(int value) throws IOException {
         fwMetadata.setInt(METADATA_ADAPTOR_IBSL, value);
         try {
             AccessController.doPrivileged(new PrivilegedExceptionAction () {
                 public Object run() throws Exception {
                     fwMetadata.save();
                     return null;
                 }
             });
         } catch (PrivilegedActionException e) {
             if (e.getException() instanceof IOException ) {
                 throw (IOException )e.getException();
             }
             throw (RuntimeException )e.getException();
         }
     }

     public AdaptorElementFactory getElementFactory() {
         if (elementFactory == null)
             elementFactory = new DefaultElementFactory();
         return elementFactory;
     }

     protected void loadMetaDataFor(DefaultBundleData data) throws IOException {
         MetaData bundleMetaData = (new MetaData(new File (data.getBundleStoreDir(), ".bundle"), "Bundle metadata")); //$NON-NLS-1$ //$NON-NLS-2$
 bundleMetaData.load();

         data.setLocation(bundleMetaData.get(METADATA_BUNDLE_LOC, null));
         data.setFileName(bundleMetaData.get(METADATA_BUNDLE_NAME, null));
         data.setGeneration(bundleMetaData.getInt(METADATA_BUNDLE_GEN, -1));
         data.setNativePaths(bundleMetaData.get(METADATA_BUNDLE_NCP, null));
         data.setStartLevel(bundleMetaData.getInt(METADATA_BUNDLE_ABSL, 1));
         data.setStatus(bundleMetaData.getInt(METADATA_BUNDLE_STATUS, 0));
         data.setReference(bundleMetaData.getBoolean(METADATA_BUNDLE_REF, false));
         data.setLastModified(bundleMetaData.getLong(METADATA_LAST_MODIFIED, 0));

         if (data.getGeneration() == -1 || data.getFileName() == null || data.getLocation() == null) {
             throw new IOException (AdaptorMsg.ADAPTOR_STORAGE_EXCEPTION); //$NON-NLS-1$
 }
     }

     public void saveMetaDataFor(AbstractBundleData data) throws IOException {
         MetaData bundleMetadata = (new MetaData(new File (((DefaultBundleData) data).createBundleStoreDir(), ".bundle"), "Bundle metadata")); //$NON-NLS-1$ //$NON-NLS-2$
 bundleMetadata.load();

         bundleMetadata.set(METADATA_BUNDLE_LOC, data.getLocation());
         bundleMetadata.set(METADATA_BUNDLE_NAME, data.getFileName());
         bundleMetadata.setInt(METADATA_BUNDLE_GEN, data.getGeneration());
         String nativePaths = data.getNativePathsString();
         if (nativePaths != null) {
             bundleMetadata.set(METADATA_BUNDLE_NCP, nativePaths);
         }
         bundleMetadata.setInt(METADATA_BUNDLE_ABSL, data.getStartLevel());
         bundleMetadata.setInt(METADATA_BUNDLE_STATUS, data.getStatus());
         bundleMetadata.setBoolean(METADATA_BUNDLE_REF, data.isReference());
         bundleMetadata.setLong(METADATA_LAST_MODIFIED, data.getLastModified());

         bundleMetadata.save();
     }

     protected void persistNextBundleID(long id) throws IOException {
         fwMetadata.setLong(METADATA_ADAPTOR_NEXTID, nextId);
         fwMetadata.save();
     }

     protected void initializeMetadata() throws IOException {
         fwMetadata = new MetaData(getMetaDataFile(), "Framework metadata"); //$NON-NLS-1$
 fwMetadata.load();
         nextId = fwMetadata.getLong(METADATA_ADAPTOR_NEXTID, 1);
         initialBundleStartLevel = fwMetadata.getInt(METADATA_ADAPTOR_IBSL, 1);
     }

     protected FrameworkLog createFrameworkLog() {
         return new DefaultLog();
     }
 }

